package com.sunday.common.core.enums.utils;

import com.sunday.common.core.enums.annotation.EnumValue;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.ObjectUtils;
import org.springframework.util.ReflectionUtils;

import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.Arrays;
import java.util.Map;
import java.util.Optional;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Collectors;

/**
 * 枚举工具类 请与 {@link EnumValue} 注解绑定
 *
 * @author zsy
 * @see EnumValue
 * @since 2023/9/7
 */
@Slf4j
public class EnumsUtils {

    private static final Map<Class<? extends Enum<?>>, Map<Object, Enum>> ENUMS_CACHE_MAP = new ConcurrentHashMap<>();

    /**
     * 目标枚举包含{@link EnumValue}注解的属性值是否存在
     *
     * @param target
     * @param value
     * @return {@link boolean}
     */
    public static boolean isValid(Class<? extends Enum<?>> target, Object value) {
        return ObjectUtils.isNotEmpty(getEnum(target, value));
    }

    /**
     * 指定枚举类中的值是否存在对应的枚举
     *
     * @param target
     * @param value
     * @return {@link T}
     */
    public static <T extends Enum<T>> T getEnum(Class<? extends Enum<?>> target, Object value) {
        return (T) getClassAdapter(target).get(value);
    }

    /**
     * 通过缓存获取枚举类，没有则新建对应枚举类缓存
     *
     * @param target
     * @return {@link Map< Object, Enum>}
     */
    public static Map<Object, Enum> getClassAdapter(Class<? extends Enum<?>> target) {
        return Optional.ofNullable(getClassMap(target))
                .orElseGet(() -> createClassMap(target));
    }

    protected static Map<Object, Enum> getClassMap(Class<? extends Enum<?>> target) {
        return ENUMS_CACHE_MAP.get(target);
    }

    /**
     * 创建枚举类缓存
     * synchronized 防止并发, 进行二次判定
     *
     * @param target
     * @return {@link Map< Object, Enum>}
     */
    protected static Map<Object, Enum> createClassMap(Class<? extends Enum<?>> target) {
        synchronized (target) {
            if (getClassMap(target) != null) {
                return getClassMap(target);
            }
            Enum<?>[] enumConstants = target.getEnumConstants();
            assert enumConstants != null : target + " not enum!";
            Field[] fields = target.getDeclaredFields();
            /**
             * 1. 检索 class中 @EnumValue 的属性
             * 2. 多个标记 @EnumValue 仅命中首个
             * 3. class中不存任何一个 @EnumValue 进行异常告警
             */
            Field field = Arrays.stream(fields)
                    .filter(f -> f.isAnnotationPresent(EnumValue.class))
                    .findFirst()
                    .orElseThrow(() -> new NullPointerException(target + " attribute annotation does not exist at @EnumValue !"));
            field.setAccessible(true);

            Map<Object, Enum> classMap = Arrays.stream(enumConstants)
                    .collect(Collectors.toMap(o -> ReflectionUtils.getField(field, o), o -> o));

            ENUMS_CACHE_MAP.put(target, classMap);
            return getClassMap(target);
        }
    }

    /**
     * isEnumClass方法接收一个Class<?>类型的参数，并返回一个布尔值。它首先使用isEnum()方法检查类是否直接为枚举类，如果是，则返回true。
     * 如果不是直接的枚举类，那么它会进一步检查类的父类是否为Enum类，并验证该类是否为最终类（即没有子类能够继承）。这是因为在Java中，枚举类继承自Enum类并被声明为最终类。
     * 通过以上两步检查，可以较为准确地判断一个类是否为枚举类。请注意，此方法假定输入的类已经加载并可访问。
     */
    public static boolean isEnumClass(Class<?> target) {
        return target.isEnum() || (target.getSuperclass() == Enum.class && Modifier.isFinal(target.getModifiers()));
    }

}
